/**
 * @file
 * @author dousha
 * @date 2020
 */

#ifndef MEMORY_H
#define MEMORY_H

#include <stdint.h>

#include "../config.h"

#ifdef NIL_IS_NULL
#define NIL ((void *) 0)
#else
#define NIL ((void *) 0xabadcafe)
#endif // NIL_IS_NULL

#ifdef MEMORY_ALIGN_4B
#define MEMORY_ALIGN_SIZE 4u
#else
#ifdef MEMORY_ALIGN_8B
#define MEMORY_ALIGN_SIZE 8u
#else
#define MEMORY_ALIGN_SIZE 1u
#endif
#endif

/**
 * @defgroup MemoryBlockFlags 内存标记
 */

/**
 * @ingroup MemoryBlockFlags
 *
 * 内存正在使用。
 */
#define MEMORY_IN_USE 0x01u
/**
 * @ingroup MemoryBlockFlags
 *
 * 用户程序内存。
 */
#define MEMORY_USER 0x10u
/**
 * @ingroup MemoryBlockFlags
 *
 * 系统程序内存。
 */
#define MEMORY_SYSTEM = 0x20u

typedef enum {
	/// 双重释放
	DOUBLE_FREE,
	/// 越权释放
	UNPRIVILEGED_FREE,
	/// 空指针释放
	NIL_FREE,
	/// 野指针释放
	WILD_FREE,
	/// 内存不足
	OUT_OF_MEMORY,
} MemoryFault;

/**
 * 内存块。
 *
 * 内存块用于表示一块连续的内存，它在被表示内存的低地址方向。
 *
 * 系统初始化完成后，有一个内存块用于表示整个可用内存空间，
 * 之后随着分配会划分更细的内存块。
 */
typedef struct MemoryBlock {
	/// 用用程序实际使用的起始内存地址
	void *base;
	/// 实际使用的长度（不包含内存块本身的长度）
	uint32_t length;
	/// 内存块标识，见 MemoryBlockFlag
	uint8_t flags;
	/// 下一块内存块的地址
	struct MemoryBlock *next;
} MemoryBlock;

/**
 * @defgroup MemoryFlags 内存初始化设置标志
 */

/**
 * @ingroup MemoryFlags
 *
 * 使用内置的 CCMRAM 作为主要内存。
 */
#define MEMORY_USE_CCMRAM 0x01u

/**
 * 内存管理器。
 */
typedef struct {
	/// 可用内存空间（字节）
	uint32_t size;
	/// 内存初始化标志位
	uint8_t flags;
	/// 实际可用内存起始地址
	void *start;
	/// 首个内存块地址
	MemoryBlock *blocks;
	/// 最后一个空闲内存块的地址
	MemoryBlock *lastFreeBlock;
} MemoryManager;

extern const void *_skylab_memory_start;

extern void memory_init();

extern void memory_protect(void *, void *);

extern void *memory_allocate(uint32_t);

extern void memory_free(void *);

extern void memory_fill(void *, uint32_t, uint8_t);

extern void memory_move(const void *, void *, uint32_t);

extern void memory_copy(const void *, void *restrict, uint32_t);

extern void *memory_align(const void *);

extern const void *memory_allocate_or_fail(uint32_t);

extern void memory_handle_access_fault(MemoryFault) __attribute__((noreturn));

#endif
